/*
    PILE (Python Implementation Library for Epy)
    Javascript representation of various Python builtins
*/

var None = null;
var False = 0, True = 1;
var abs = Math.abs;

function dir(obj) {
    var lst = list();
    for(var i in obj) {
        lst.append(i);
    }
    return lst;
}

function max(obj) {
    var best = None;
    for(var __epy_iter_i = obj.__iter__();;) {
        try {
            var i = __epy_iter_i.next();
            if(best == None || i > best) {
                best = i;
            }
        } catch(e) {
            if(e == "StopIteration") break;
            else throw e;
        }
    }
    return best;
}

function min(obj) {
    var best = None;
    for(var __epy_iter_i = obj.__iter__();;) {
        try {
            var i = __epy_iter_i.next();
            if(best == None || i < best) {
                best = i;
            }
        } catch(e) {
            if(e == "StopIteration") break;
            else throw e;
        }
    }
    return best;
}

function sum(obj) {
    var value = 0;
    for(var __epy_iter_i = obj.__iter__();;) {
        try {
            value += __epy_iter_i.next();
        } catch(e) {
            if(e == "StopIteration") break;
            else throw e;
        }
    }
    return value;
}

String.prototype.__getitem__ = function(i) {
    return this.charAt(i);
}

String.prototype.__setitem__ = function(i, c) {
    throw "'str' object does not support item assignment";
}

String.prototype.__iter__ = function() {
    var self = this;
    var iter = new Object();
    iter.i = 0;
    iter.next =  function() {
        if(iter.i < self.length) {
            var ret = self.charAt(iter.i);
            iter.i++;
            return ret;
        } else {
            throw "StopIteration";
        }
    }
    return iter;
}

Array.prototype.__getitem__ = function(key) {
    return this[key];
}

function len(obj) {
    try {
        return obj.__len__();
    } catch (e) {
        return obj.length
    }
}

function int(obj) {
    try {
        return obj.__int__();
    } catch (e) {
        return parseInt(obj);
    }
}

function float(obj) {
    try {
        return obj.__float__();
    } catch (e) {
        return parseFloat(obj);
    }
}

function str(obj) {
    if(typeof(obj) == "string") {
        return obj;
    } else {
        try {
            return obj.__str__();
        } catch(e) {
            try {
                return obj.toString();
            } catch(e2) {
                return obj;
            }
        }
    }
}

function type(obj) {
    try {
        return obj.__type__;
    } catch (e) {
        return typeof(obj);
    }
}

/*
Python-style list object
    arg(0 ... n):
        Initial objects in list
*/
function list() {
    var self = new Object;

    self.data = [];

    for(var i = 0; i < arguments.length; ++i) {
        self.data.push(arguments[i]);
    }

    self.__len__ = function() {
        return self.data.length;
    }

    self.__getitem__ = function(key) {
        if(key < 0) {
            return self.data[self.data.length + key];
        } else {
            return self.data[key];
        }
    }

    self.__setitem__ = function(key, value) {
        self.data[key] = value;
    }

    self.__delitem__ = function(key) {
        self.data.splice(key, 1);
    }

    self.__getslice__ = function(i, j) {
        var array = self.data.slice(i, j);
        var lst = list();
        for(var i = 0; i < array.length; ++i) {
            lst.append(array[i]);
        }
        return lst;
    }

    self.__iter__ = function() {
        var iter = new Object();
        iter.i = 0;
        iter.next =  function() {
            if(iter.i < self.data.length) {
                var ret = self.data[iter.i];
                iter.i++;
                return ret;
            } else {
                throw "StopIteration";
            }
        }
        return iter;
    }

    self.append = function(value) {
        self.data.push(value);
    }

    self.extend = function(obj) {
        for(var __epy_iter_i = obj.__iter__();;) {
            try {
                self.data.push(__epy_iter_i.next());
            } catch(e) {
                if(e == "StopIteration") break;
                else throw e;
            }
        }
    }

    self.__str__ = function() {
        var out = "";
        for(var i = 0; i < self.data.length; ++i) {
            if(typeof(self.data[i]) == "string") {
                out += "\"" + self.data[i] + "\", ";
            } else {
                out += str(self.data[i]) + ", ";
            }
        }
        return "[" + out.substr(0, out.length - 2) + "]";
    }

    self.__type__ = "list";

    return self;
}

/*
    Temporary solution, needs improved!
*/
function tuple() {
    var lst = list();

    for(var i = 0; i < arguments.length; ++i) {
        lst.append(arguments[i]);
    }

    self.__type__ = "tuple";

    return lst;
}

/*
Python-style dict object
    arg0:
        Initial objects in dict (as one argument, a Js dict)
*/
function dict() {
    var self = new Object();

    self.dict = {};

    if(arguments.length == 1) {
        for(var i in arguments[0]) {
            self.dict[i] = arguments[0][i];
        }
    }

    self.__len__ = function() {
        return self.dict.length;
    }

    self.__getitem__ = function(key) {
        return self.dict[key];
    }

    self.__setitem__ = function(key, value) {
        self.dict[key] = value;
    }

    self.__delitem__ = function(key) {
        self.dict.splice(key, 1);
    }

    self.__iter__ = function() {
        var iter = new Object();
        iter.i = 0;
        iter.keys = list();
        for(var i in self.dict) {
            iter.keys.append(i);
        }
        iter.next =  function() {
            if(iter.i < len(iter.keys)) {
                var ret = iter.keys.__getitem__(iter.i);
                iter.i++;
                return ret;
            } else {
                throw "StopIteration";
            }
        }
        return iter;
    }

    self.__str__ = function() {
        var out = "";
        for(var i in self.dict) {
            out += str(i) + ": " + str(self.dict[i]) +", ";
        }
        return "{" + out.substr(0, out.length - 2) + "}";
    }

    self.has_key = function(key) {
        return (key in self.dict)
    }

    self.__type__ = "dict";

    return self;
}

/*
Python-style xrange function
(Returns integer iterator)
    arg0:
        Index to start iteration
    arg1:
        Index to end iteration
    arg2:
        Step of iteration
*/
function xrange() {
    var self = new Object();
    self.start = 0;
    self.end = 0;
    self.step = 1;

    if(arguments.length == 1) {
        self.end = arguments[0];
    } else if(arguments.length == 2) {
        self.start = arguments[0];
        self.end = arguments[1];
    } else if(arguments.length == 3) {
        self.start = arguments[0];
        self.end = arguments[1];
        self.step = arguments[2];
    }

    self.__iter__ = function() {
        self.index = self.start;
        return self;
    }

    self.next = function() {
        if(self.index < self.end) {
            var ret = self.index;
            self.index += self.step;
            return ret;
        } else {
            throw "StopIteration";
        }
    }

    self.__type__ = "xrange";

    return self;
};

/*
Python-style range function
(Returns list)
    arg0:
        Index to start iteration
    arg1:
        Index to end iteration
    arg2:
        Step of iteration
*/
function range() {
    var self = list();
    var start = 0;
    var end = 0;
    var step = 1;

    if(arguments.length == 1) {
        end = arguments[0];
    } else if(arguments.length == 2) {
        start = arguments[0];
        end = arguments[1];
    } else if(arguments.length == 3) {
        start = arguments[0];
        end = arguments[1];
        step = arguments[2];
    }

    for(var i = start; i < end; i += step) {
        self.append(i);
    }

    self.__type__ = "range";

    return self;
};

/*
Implementation of Python-style for statement
(iterables throw an exception to break loop!)
    iter:
        Object to iterate over
    body:
        Function called for each item in iter (item passed as parameter)
*/
function pyFor(iter, body) {
    var i = iter.__iter__();
    for(;;) {
        try {
            body(i.next())
        } catch (e) {
            if(e == "StopIteration") {
                break;
            }
        }
    }
}

/*
Returns list built by Python-style list comprehension
    iter:
        Iterable object
    body:
        Function returning assignment for each item
    cond:
        Condition for assignment, or null for no condition (function returning
        true or false
*/
function pyListComp(iter, body, cond) {
    var lst = list();
    var i = iter.__iter__();
    if(!cond) {
        for(;;) {
            try {
                lst.append(body(i.next()));
            } catch (e) {
                break;
            }
        }
    } else {
        for(;;) {
            try {
                var value = i.next();
                if(cond(value)) {
                    lst.append(body(value));
                }
            } catch (e) {
                break;
            }
        }
    }
    return lst;
}

/*
Returns list built by Python-style slice
    obj:
        Indexible object being sliced
    start:
        Starting index of slice
    end:
        Ending index of slice
    step:
        Step of slice
*/
function pyGetSlice(obj, start, end, step) {
    var lst = list();
    if(end <= 0) {
        end += len(obj);
    }
    for(var i = start; i < end; i += step) {
        lst.append(obj.__getitem__(i));
    }
    return lst;
}

/*
Returns list built by Python-style slice assignment
    obj:
        Indexible object being sliced
    start:
        Starting index of slice
    end:
        Ending index of slice
    iter:
        Iterable object to replace slice with
*/
function pySetSlice(obj, start, end, iter) {
    var lst = list();
    for(var i = 0; i < start; ++i) {
        lst.append(obj.__getitem__(i));
    }
    pyFor(iter, function(i) {
        lst.append(i);
    }, null);
    for(var i = end; i < len(obj); ++i) {
        lst.append(obj.__getitem__(i));
    }
    return lst;
}

/*
Returns string using C-style format
    arg0:
        Format string
    arg(1 ... n):
        Arguments
*/
function pyStrFormat(s, args) {
    // Hack for non-tuple (single) arguments
    try {
        args.__getitem__(0);
        if(typeof(args) == "string") {
            args = tuple(args);
        }
    } catch(e) {
        args = tuple(args);
    }
    var a = 0, c = 0, out = "";
    for(var i = 0; i < s.length; ++i) {
        c = s.charAt(i);
        if(c == "%") {
            c = s.charAt(++i);
            if(c == "i" || c == "s" || c == "f") {
                out += str(args.__getitem__(a));
                a++;
            } else if (c == "%") {
                out += "%";
            }
        } else {
            out += c;
        }
    }
    return out;
}
